﻿using UnityEngine;
using System.Collections;

public class MoveConst
{
	public static float minDistance = 0.0000001f;
	public static float minAngel = 0.1f;
}

/*
 * 移动辅助文件
 */
public class LookAtHelper
{
	public Square targetSqr;
	public delegate void LookAtMonitor(bool bLookAt);
	public LookAtMonitor monitor;

	float rotateStartTime;
	Quaternion startQuater;
	Vector3 startPosition;
	Vector3 dstPosition;
	Quaternion rotateQuater;
	float rotateAngle;
	bool working = false;
	
	public bool IsLookAt(float distance, float angle)
	{
		// return true if already get position
		if (distance < MoveConst.minDistance)
			return true;
		else{
			if(angle > MoveConst.minAngel)
				return false;
			else
				return true;
		}
	}
	
	public void StartLookAt(Vector3 targetPos)
	{
		dstPosition = targetPos;
		working = false;
	}
	
	public void ProcessLookAt(float turnspeed, float distance, float angle)
	{
		if (IsLookAt(distance, angle))
			return;
		else{
			if (!working){
				working = true;
				rotateStartTime = Time.time - Time.deltaTime;
				startQuater = targetSqr.transform.rotation;
				startPosition = targetSqr.VProjectPosition;
				rotateQuater =  Quaternion.LookRotation (dstPosition - startPosition);
				rotateAngle = Quaternion.Angle(startQuater, rotateQuater);

				if (null != monitor)
					monitor(true);
			}	
		}
		
		if(working)
		{
			float frac = (Time.time - rotateStartTime) * turnspeed / rotateAngle;
			
			if (frac >= 1)
			{
				frac = 1;
				working = false;

				if (null != monitor)
					monitor(false);
			}
			
			targetSqr.transform.rotation = Quaternion.Slerp (startQuater, rotateQuater, frac);		
		}
	}
}

public class TranslateHelper
{
	public Square targetSqr;
	public delegate void TranslateMonitor(bool bTranslate);
	public TranslateMonitor monitor;

	float moveStartTime;
	Vector3 startPosition;
	Vector3 dstPosition;
	float moveLength;
	bool working = false;
	
	public bool NeedTranslate(float distance, float angle)
	{
		if (distance > MoveConst.minDistance)
			return false;
		else
			return true;
	}
	
	public bool CanTranslate(float angle)
	{
		if (angle < 180)
			return true;
		else
			return false;
	}
	
	public void StartTranslate(Vector3 targetPos)
	{
		dstPosition = targetPos;
		working = false;
	}
	
	public void ProcessTranslate(float movespeed, float distance, float angle)
	{
		if (NeedTranslate(distance, angle))
			return;
		else{
			if (CanTranslate(angle))
			{
				if (!working){
					working = true;
					moveStartTime = Time.time - Time.deltaTime;
					startPosition = targetSqr.VProjectPosition;
					moveLength = Vector3.Distance(startPosition, dstPosition);

					if (null != monitor)
						monitor(true);
				}	
			}
		}
	
		if(working)
		{
			float frac = (Time.time - moveStartTime) * movespeed / moveLength;
			
			if (frac >= 1)
			{
				frac = 1;
				working = false;

				if (null != monitor)
					monitor(false);
			}
			
			targetSqr.VProjectPosition = Vector3.Lerp(startPosition, dstPosition, frac);	
		}
	}
}

public class RotateHelper
{
	public Square targetSqr;
	public delegate void RotateMonitor(bool bRotate);
	public RotateMonitor monitor;

	float rotateStartTime;
	Quaternion startQuater;
	Quaternion destinationQuater;
	float rotateAngle;
	bool working = false;
	
	public bool IsOrientations(float angle)
	{
		if (angle < MoveConst.minAngel)
			return true;
		else
			return false;
	}
	
	public bool CanOrientations(float distance)
	{
		if (distance > MoveConst.minDistance)
			return false;
		else
			return true;
	}
	
	public void StartRotate(Quaternion dstQuater)
	{
		destinationQuater = dstQuater;
		working = false;
	}
	
	public void ProcessRotate(float turnspeed, float distance, float angle)
	{
		if (IsOrientations(angle))
			return;
		else{
			if (CanOrientations(distance))
			{
				if (!working){
					working = true;
					startQuater = targetSqr.transform.rotation;
					rotateAngle = Quaternion.Angle(startQuater, destinationQuater);
					rotateStartTime = Time.time;

					if (null != monitor)
						monitor(true);
				}		
			}
			else
			{
				working = false;
			}
		}
		
		if (working)
		{
			float frac = (Time.time - rotateStartTime) * turnspeed / rotateAngle;
			
			if (frac >= 1)
			{
				frac = 1;
				working = false;

				if (null != monitor)
					monitor(false);
			}
			
			targetSqr.transform.rotation = Quaternion.Slerp (startQuater, destinationQuater, frac);
		}
	}
}

public enum ESquareMoveState
{
	eStartLookAt,
	eStopLookAt,
	eStartTranslate,
	eStopTranslate,
	eStartRotation,
	eStopRotation,
}

public class MsgSquareMove : System.EventArgs
{
	public MsgSquareMove(ESquareMoveState eAct)
	{
		eAction = eAct;
	}

	public ESquareMoveState eAction;
}

public class SquareMove : MonoBehaviour {

	float speed;
	float turnspeed;
	
	Vector3 dstPosition;
	public Vector3 DstPosition
	{
		get { return dstPosition;}
	}
	
	Quaternion dstOrientation;
	public Quaternion DstOrientation
	{
		get { return dstOrientation;}
	}

	public Square sqr;
	public VirtualJoystick vjs;
	public event System.EventHandler<MsgSquareMove> evtSqMov;
	
	LookAtHelper lookAtHelper;
	TranslateHelper moveHelper;
	RotateHelper orientationsHelper;

	// Use this for initialization
	void Start () {
		speed = GetComponent<CommonProperty>().moveSpeed;
		turnspeed = GetComponent<CommonProperty>().turnSpeed;

		dstPosition = sqr.VProjectPosition;
		dstOrientation = sqr.transform.rotation;
		
		lookAtHelper = new LookAtHelper();
		lookAtHelper.targetSqr = sqr;
		lookAtHelper.monitor = OnSquareLookAt;
		
		moveHelper = new TranslateHelper();
		moveHelper.targetSqr = sqr;
		moveHelper.monitor = OnSquareTranslate;
		
		orientationsHelper = new RotateHelper();
		orientationsHelper.targetSqr = sqr;
		orientationsHelper.monitor = OnSquareRotation;
		
		vjs.evtRotation += OnSetDstOrientation;
	}
	
	// Update is called once per frame
	void Update () {
		
		Vector3 myposition = sqr.VProjectPosition;
		
		float distance = Vector3.Distance(myposition, dstPosition);
		
		float angle = 0;
		if(distance > MoveConst.minDistance)
		{
			Quaternion dstqt = Quaternion.LookRotation (dstPosition - myposition);
			angle = Quaternion.Angle (dstqt, sqr.transform.rotation);
		}

		lookAtHelper.ProcessLookAt(turnspeed, distance, angle);
		moveHelper.ProcessTranslate(speed, distance, angle);
		
		float rotateAngle = 0;
		rotateAngle = Quaternion.Angle (dstOrientation, sqr.transform.rotation);
		
		orientationsHelper.ProcessRotate(turnspeed, distance, rotateAngle);
	}

	// return true if new destination is accept.
	// false if new destination is not vaild.

	public bool SetDestination (Vector3 dstPos)
	{
		Vector3 testPos = dstPos;
		testPos.y = 0;

		if (TooClose2StartPositon(testPos))
			return false;
		
		if (TooClose2CurDst(testPos))
			return false;

		dstPosition = testPos;
		
		BeginMove ();
		
		return true;
	}
	
	void BeginMove()
	{
		lookAtHelper.StartLookAt(dstPosition);
		moveHelper.StartTranslate(dstPosition);
	}
	
	void OnSetDstOrientation(object sender, EvtVJS vjsEvent)
	{
		if (GetComponent<Camp>().Equals(vjsEvent.campInfo))
		{
			dstOrientation = vjsEvent.ToSqrQuat;
			orientationsHelper.StartRotate(dstOrientation);
		}
			
	}

	bool IsFaceToDst(){
		
		Vector3 myposition = sqr.VProjectPosition;
		
		// alraedy reach destination,return true
		if (dstPosition.Equals(myposition))
			return true;
		
		Quaternion dstqt = Quaternion.LookRotation (dstPosition - myposition);
		
		float angle = Quaternion.Angle (dstqt, sqr.transform.rotation);
		
		// less than one duration rotation
		if(angle <= 0.1)
		{
			return true;
		}
		
		return  false;
	}

	// new destination is not valid if too close to stay position or last dst position
	bool TooClose2CurDst(Vector3 testDst)
	{
		float distance = Vector3.Distance (testDst, dstPosition);

		if (distance < MoveConst.minDistance)
		{
			return true;
		}
		
		return false;
	}

	bool TooClose2StartPositon(Vector3 testDst)
	{
		float distance = Vector3.Distance (testDst, sqr.VProjectPosition);
		
		if (distance < MoveConst.minDistance)
		{
			return true;
		}

		return false;
	}

	void OnSquareLookAt(bool bLookAt)
	{
		if (bLookAt)
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStartLookAt));
		else
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStopLookAt));
	}

	void OnSquareTranslate(bool bTranslate)
	{
		if (bTranslate)
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStartTranslate));
		else
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStopTranslate));
	}

	void OnSquareRotation(bool bRotation)
	{
		if (bRotation)
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStartRotation));
		else
			evtSqMov(this, new MsgSquareMove(ESquareMoveState.eStopRotation));
	}
}
